package game.map;

import game.mechanics.Item;
import game.units.Character;
import game.units.Monster;
import game.units.Unit;
import game.util.Die;

import java.util.Random;
import java.util.Stack;

import processing.core.PApplet;

///////////////////////////////////////////////////////////////////////
// Attributes and methods for use on the map                         //
///////////////////////////////////////////////////////////////////////

public class MapUnit implements Comparable<MapUnit> {
	
	private static final int HEROMARGIN = 40;
	private static final int PLANTOGOMARGIN = 5;
	
	private long id;
	
	private Integer[] pos;
	private int facing;
	private int group;
	private int color;
	
	private Stack<Integer[]> planToGo;
	private boolean canStayHere;
	
	private int maxGo;
	private int initiative;
	
	private boolean dead = false;
	
	private Unit unit;
	
	private void init(int x, int y, int color, int group) {
		
		planToGo = new Stack<Integer[]>();
		canStayHere = true;
		maxGo = 50;
		
		Integer[] pos = { x, y };
		setPosition(pos);
		facing = 0;
		this.color = color;
		setGroup(group);
		
	}
	//  load Monster from map
	public MapUnit(int x, int y, int color, int group, long mapId, 
			String monsterName) {
		init(x, y, color, group);
		this.id = mapId;
		// monta a unit
		unit = Monster.getMonsters().get(monsterName);
	}

	// instantiate monster on editor
	public MapUnit(int x, int y, int color, int group, String monsterName) {
		init(x, y, color, group);
		generateId();
		unit = Monster.getMonsters().get(monsterName);
	}

	// add hero to map
	public MapUnit(int x, int y, int color, int group, Unit character) {
		init(x, y, color, group);
		unit = character;
		this.id = character.getId();
	}

	public int getX() {
		return pos[0];
	}
	public int getY() {
		return pos[1];
	}

	public void setPosition(int x, int y) {
		Integer[] pos = {x, y};
		setPosition(pos);
	}
	
	public void setPosition(Integer[] pos) {
		this.pos = pos;
		planToGo.clear();
	}

	public Integer[] getPlannedPos() {
		if (planToGo.isEmpty())
			return pos;
		return planToGo.lastElement();
	}
	
	public int getPlannedX() {
		return getPlannedPos()[0];
	}
	
	public int getPlannedY() {
		return getPlannedPos()[1];
	}
	
	public void undoMoves() {
		planToGo.clear();
	}
	
	public boolean canPass(MapUnit unit) {
		return group == unit.getGroup();
	}
	public int getGroup() {
		return group;
	}
	public void setGroup(int group) {
		this.group = group;
	}

	public void planToGo(int x, int y) {
		
		if (planToGo.size() > maxGo - 1)
			return;
		
		Integer[] plan = {x, y};
		planToGo.push(plan);
		canStayHere = false;
	}
	
	public void canStayHere(boolean can) {
		canStayHere = can;
	}
	
	public boolean canStayThere() {
		return canStayHere;
	}

	public void face(int facing) {
		this.facing = facing;
	}

	public int getFacing() {
		return facing;
	}
	
	public void draw(PApplet app, int tileSide) {
		
		app.stroke(color);
		app.fill(WsApplet.GRAY);
		
		drawToken(app, tileSide, getX()*tileSide, getY()*tileSide);
		
	}
	
	private void drawToken(PApplet app, int tileSide, int x, int y) {
		
		int centerX = x + tileSide/2;
		int centerY = y + tileSide/2;
		
		int heroDiameter = tileSide - HEROMARGIN * tileSide/100;
		app.ellipse(centerX, centerY, heroDiameter, heroDiameter);
		
	}
	
	public void drawGhost(WsApplet app, int tileSide, int centerX, int centerY) {
		
		app.stroke(color + Static.transp);
		app.fill(WsApplet.GRAY + Static.transp);
		
		drawToken(app, tileSide, app.mouseX - tileSide/2 - centerX, 
				app.mouseY - tileSide/2 - centerY);
		
	}
	
	/**
	 * Usado para desenhar os elementos de planToGo
	 * @param app
	 * @param visibility 
	 * @param w
	 */
	public void drawExtra(PApplet app, int tileSide, int[][] visibility) {
		
		app.stroke(color);
		app.fill(WsApplet.GRAY);
		
		int planDiameter = tileSide / PLANTOGOMARGIN;
		int x = getPlannedX();
		int y = getPlannedY();
		if (!planToGo.isEmpty() && (visibility == null || visibility[x][y] == 1))
			app.ellipse(planToGo.lastElement()[0]*tileSide + tileSide/2, 
					planToGo.lastElement()[1]*tileSide + tileSide/2, 
					planDiameter+20, planDiameter+20);
		for (Integer[] plan : planToGo)
			if (visibility == null || visibility[plan[0]][plan[1]] == 1)
				app.ellipse(plan[0]*tileSide + tileSide/2, 
						plan[1]*tileSide + tileSide/2, planDiameter, planDiameter);

	}
	
	public void drawGlow(WsApplet app, int tileSide) {
//		app.stroke(color);
		app.noFill();
		app.rect(getX()*tileSide, getY()*tileSide, tileSide, tileSide);
	}

	public String commitMove() {
		if (planToGo.isEmpty())
			return null;
		
		String movement = getPlannedX() + "," + getPlannedY();
		setPosition(planToGo.lastElement());
		return movement;
	}
	
	public void generateId() {
		this.id = (new Random()).nextLong();
	}
	
	public void setId(long id) {
		this.id = id;
	}
	
	public long getId() {
		return id;
	}

	public void pickItem(Item item) {
		
		if (!(unit instanceof Character))
			return;
		
		Character chara = ((Character) unit);
		chara.pickItem(item);
		
	}
	
	public void rollInitiative() {
		initiative = unit.getInitiative() + Die.roll(20, 1);
	}
	
	private int getInitiative() {
		return initiative;
	}

	public int getColor() {
		return color;
	}
	
	public String getName() {
		return getUnit().getName();
	}
	
	public Unit getUnit() {
		return unit;
	}

	public void setDead(boolean dead) {
		this.dead = dead;
	}
	
	public boolean isDead() {
		return dead;
	}

	@Override
	public int compareTo(MapUnit mu) {
		return initiative - mu.getInitiative();
	}

}
